package contegor.scire.core.queries;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.CountDownLatch;

import contegor.scire.core.Connectable;
import contegor.scire.core.Container;
import contegor.scire.core.Id;
import contegor.scire.core.ReturnPoint;
import contegor.scire.core.ReturnPointAdapter;
import contegor.scire.core.Task;
import contegor.scire.core.TaskId;
import contegor.scire.core.Version;
import contegor.scire.core.WrongStateException;

/**
 * {@link Query} that returns the intersection of other queries.
 *
 * @version $Revision: 1.4 $
 * @author  Héctor Cirbián Pousa ($Author: hector $)
 */
@Version("$Id: Intersection.java,v 1.4 2010-02-01 10:55:41 hector Exp $")
public class Intersection<I extends Connectable> extends QueryWithDependencies<I,I> {
  private static final long serialVersionUID = -889505849682401204L;

  private class CollectorReturnPoint extends ReturnPointAdapter<I> {

    private Set<I> results = new ConcurrentSkipListSet<I>();

    public void finish(TaskId taskId) {
      super.finish(taskId);
      Intersection.this.counter.countDown();
    }

    public <Y extends I> void manage(TaskId taskId, Y result) {
      results.add(result);
    }

    public <Y extends I> void manage(TaskId taskId, Collection<Y> result) {
      results.addAll(result);
    }

    public void manage(TaskId taskId, Exception exception) {
      super.manage(taskId, exception);
      Intersection.this.returnPoint.manage(taskId, exception);
    }

    public Set<I> getResults() {
      return results;
    }
  };

  private List<CollectorReturnPoint> dependenciesReturnPoints;
  private CountDownLatch             counter;

  /**
   * This constructor make sense to eliminate duplicate results from a {@link Query}.
   */
  public Intersection(Query<I> dependencie) {
    super(dependencie);
    this.counter = new CountDownLatch(this.dependencies.size());
    fillDependenciesReturnPoints();
  }

  public Intersection(Id targetContainerId, Query<I> ... dependencies) {
    super(targetContainerId, dependencies);
    this.counter = new CountDownLatch(this.dependencies.size());
    fillDependenciesReturnPoints();
  }

  private void fillDependenciesReturnPoints() {    
    this.dependenciesReturnPoints = new ArrayList<CollectorReturnPoint>(this.dependencies.size());
    for (int i=0; i<this.dependencies.size(); i++)
      this.dependenciesReturnPoints.add(new CollectorReturnPoint());
  }

  public void process(Container container) throws WrongStateException {
    try {
      counter.await();
      Set<I> finalResult = null;
      for (CollectorReturnPoint crp: dependenciesReturnPoints) {
        if (finalResult == null)
          finalResult = crp.getResults();
        else
          finalResult.retainAll(crp.getResults());
      }
      returnPoint.manage(this.taskId, finalResult);
    }
    catch (InterruptedException ex) {
      throw new WrongStateException(ex);
    }
  }

  @Override
  public List<? extends Task<I>> getDependencies() {
    return this.dependencies;
  }

  @Override
  public List<? extends ReturnPoint<I>> getDependenciesReturnPoints() {
    return this.dependenciesReturnPoints;
  }
}